using Godot;
using System;
using System.Collections.Generic;
using System.Text;
using Common;

namespace DRE.TextWriter {
	public class TextWriter : Control {
		static CommandManager command = new CommandManager();

		public TextWriter() {
			Voice = "None";
		}
		public void Restart() {
			Clear();
			Speed = 15;
			Paused = false;
			Instant = false;
			Skippable = true;
			Index = 0;
			Time = 0;
			VerticalAlign = VAlign.Top;
			HorizontalAlign = HAlign.Left;
			InstanceColor = "White";
			InstanceFont = "Default";
			InstanceEffect = "None";
			InstanceShadow = false;
			InstanceScale = new Vector2(2, 2);
			InstanceSpacing = new Vector2(0, 0);
		}
		[Export(PropertyHint.MultilineText)]
		public string Text { get; set; } = "";
		public float Speed { get; set; } = 15;
		string voice;
		string[] voiceParams;
		public string Voice {
			get => voice;
			set {
				voice = value;
				voiceParams = voice.Split('.');
			}
		}
		public bool Paused { get; set; } = false;
		public bool Instant { get; set; } = false;
		public bool Skippable { get; set; } = true;
		public int Index { get; set; } = 0;
		public float Time { get; set; } = 0;
		public VAlign VerticalAlign { get; set; } = VAlign.Top;
		public HAlign HorizontalAlign { get; set; } = HAlign.Left;
		public Vector2 MinSize { get; private set; } = new Vector2(0, 0);

		public string InstanceColor { get; set; } = "White";
		public string InstanceFont { get; set; } = "Default";
		public Vector2 InstancePosition { get; set; } = new Vector2(0, 0);
		public string InstanceEffect { get; set; } = "None";
		public bool InstanceShadow { get; set; } = false;
		public Vector2 InstanceScale { get; set; } = new Vector2(2, 2);
		public Vector2 InstanceSpacing { get; set; } = new Vector2(0, 0);

		public List<TextInstance> Instances { get; } = new List<TextInstance>();

		AudioStreamPlayer audio = null;
		Node2D instanceRoot = null;
		PackedScene instanceTemplate = null;
		public override void _Ready() {
			audio = GetNode<AudioStreamPlayer>("Audio");
			instanceRoot = GetNode<Node2D>("Instances");
			instanceTemplate = GD.Load<PackedScene>("res://Generic/TextWriter/TextInstance.tscn");
		}

		public override void _Process(float delta) {
			if (Index >= Text.Length) {
				return;
			}

			if (Paused) {
				Instant = false;
				Time = 0;
				if (Input.IsActionJustPressed("Confirm")) {
					Paused = false;
				}
			} else if (Skippable && Input.IsActionJustPressed("Cancel")) {
				Instant = true;
			} else if (Instant) {
				Time = 0;
			} else if (Time > 0) {
				Time -= delta;
			}

			bool voicePlayed = false;
			while (Index < Text.Length && !Paused && (Time <= 0 || Instant)) {
				string currentChar = Text[Index].ToString();
				if (currentChar == "{") {
					Index += 1;

					bool stringMode = false;
					StringBuilder sb = new StringBuilder();
					while (Index < Text.Length) {
						currentChar = Text[Index].ToString();
						Index += 1;

						if (currentChar != "}" || stringMode) {
							sb.Append(currentChar);
							if (currentChar == "\"") {
								stringMode = !stringMode;
							}
						} else {
							break;
						}
					}
					command.Execute(this, sb.ToString());
				} else {
					Print(currentChar);
					if (!Settings.SpecialChars.ContainsKey(currentChar) || Settings.SpecialChars[currentChar].Time) {
						Time += 1f / Speed;

						if (!Instant && !voicePlayed) {
							Settings.GetVoiceProcessor(voiceParams.Length > 0 ? voiceParams[0] : string.Empty).OnWrite(this, voiceParams, audio);
							voicePlayed = true;
						}
					}
					Index += 1;
				}
			}
		}

		Vector2 GetTextSize(string text, Settings.FontResource font) {
			Font fobj = GD.Load<Font>(font.Path);
			Vector2 size = (fobj.GetStringSize(text) + font.Spacing + InstanceSpacing) * font.Scale * InstanceScale;
			return new Vector2(Mathf.Ceil(size.x), Mathf.Ceil(size.y));
		}
		public void Clear() {
			foreach (TextInstance obj in Instances) {
				obj.QueueFree();
			}
			Instances.Clear();
			InstancePosition = new Vector2(0, 0);
			MinSize = new Vector2(0, 0);
		}
		void NewLine() {
			InstancePosition = new Vector2(0, InstancePosition.y + GetTextSize("A", Settings.GetFontSet(InstanceFont).ASCII).y);
		}
		void Print(string text) {
			if (text == "\n") {
				NewLine();
			} else {
				bool visible = (!Settings.SpecialChars.ContainsKey(text) || Settings.SpecialChars[text].Visible);

				//字体
				Settings.FontSet fs = Settings.GetFontSet(InstanceFont);
				Settings.FontResource fontRes = fs.GetFontResource(text[0]);

				if (visible) {
					//实例化
					TextInstance inst = instanceTemplate.Instance() as TextInstance;
					instanceRoot.AddChild(inst);
					inst.Text = text;
					inst.Color = InstanceColor;
					inst.Shadow = InstanceShadow;
					inst.Effect = InstanceEffect;
					inst.OriginalScale = InstanceScale;
					inst.Font = InstanceFont;

					float offsetY = 0;
					if (text[0] > byte.MaxValue) {
						float hUnicode = GetTextSize(text, fs.Unicode).y;
						float hASCII = GetTextSize(text, fs.ASCII).y;
						offsetY = (hUnicode - hASCII) / 2f;
					}

					inst.OriginalPosition = InstancePosition - Vector2.Down * offsetY;
					Instances.Add(inst);
				}

				//计算下一位置
				InstancePosition += Vector2.Right * GetTextSize(text, fontRes).x;
			}
			if (InstancePosition.x > MinSize.x) {
				MinSize = new Vector2(Mathf.Max(MinSize.x,InstancePosition.x), Mathf.Max(MinSize.y,InstancePosition.y));
			}
			Vector2 pos=new Vector2(0,0);
			switch(HorizontalAlign){
				case HAlign.Left:
					pos.x=0;
					break;
				case HAlign.Center:
					pos.x=-MinSize.x/2+RectSize.x/2;
					break;
				case HAlign.Right:
					pos.x=-MinSize.x+RectSize.x;
					break;
			}
			switch (VerticalAlign) {
				case VAlign.Top:
					pos.y = 0;
					break;
				case VAlign.Center:
					pos.y = -MinSize.y / 2+RectSize.y/2;
					break;
				case VAlign.Bottom:
					pos.y = -MinSize.y+RectSize.y;
					break;
			}
			instanceRoot.Position=pos;
		}
	}
}
